// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package zipkin

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"gitee.com/go-wares/log/base"
	"gitee.com/go-wares/log/conf"
	"gitee.com/go-wares/log/plugins/reporters/zipkin/model"
)

// Formatter
// is a component that convert span as byte array.
type Formatter struct{}

// Format
// log item as string.
func (o *Formatter) Format(spans []base.Span) (body []byte) {
	c := conf.Get()
	l := make([]*model.SpanModel, 0)
	for _, span := range spans {
		if v := o.GenerateSpan(c, span); v != nil {
			l = append(l, v)
		}
	}
	if len(l) > 0 {
		if buf, err := json.Marshal(l); err == nil {
			return buf
		}
	}
	return
}

func (o *Formatter) GenerateSpan(c *conf.Config, span base.Span) (m *model.SpanModel) {
	m = &model.SpanModel{
		SpanContext: model.SpanContext{
			TraceID: model.TraceID{
				High: binary.BigEndian.Uint64(span.Tracing().TraceId.Byte()[:8]),
				Low:  binary.BigEndian.Uint64(span.Tracing().TraceId.Byte()[8:]),
			},
			ID: model.ID(binary.BigEndian.Uint64(span.Tracing().SpanId.Byte())),
		},
		Name:          span.Name(),
		Kind:          model.Client,
		Timestamp:     span.StartTime(),
		Duration:      span.Duration(),
		Shared:        false,
		LocalEndpoint: &model.Endpoint{ServiceName: c.ServiceName},
		Annotations:   make([]model.Annotation, 0),
		Tags:          make(map[string]string),
	}

	// Append parent id.
	if v := span.Tracing().ParentSpanId; v != nil {
		x := model.ID(binary.BigEndian.Uint64(v.Byte()))
		m.SpanContext.ParentID = &x
	}

	// Local endpoint.
	m.LocalEndpoint = &model.Endpoint{
		ServiceName: c.ServiceName,
	}

	// Tag system.
	for k, v := range map[string]any{
		"service.addr":    c.ServiceAddr,
		"service.pid":     c.Pid,
		"service.port":    c.ServicePort,
		"service.name":    c.ServiceName,
		"service.version": c.ServiceVersion,
	} {
		m.Tags[k] = fmt.Sprintf(`%v`, v)
	}

	// Tag user.
	for k, v := range span.GetAttr() {
		m.Tags[k] = fmt.Sprintf(`%v`, v)
	}

	// Annotations.
	for _, log := range span.Logs() {
		m.Annotations = append(m.Annotations, model.Annotation{
			Timestamp: log.Time,
			Value:     fmt.Sprintf(`[%s] %s`, log.Level, log.String()),
		})
	}
	return
}
